import vars
import rate
import binascii
import hashlib
import re
import buddy


from vars import *
from binascii import *
from hashlib import md5
from rate import *
from buddy import *


class snac():
    family = ""
    subtype = ""
    flags = ""
    request  = ""
    data = ""
    tlvs = ""
    important = ""

    def __init__(self):
        family = ""
        subtype = ""
        flags =""
        request = ""
        data = ""
        tlvs =""

    #Get size of snac packet, needed to pack contents into flap
    def getSize(self):
        size = len(self.family) + len(self.subtype)+len(self.flags)+len(self.request)+len(self.data)
        size = size/2
        size = "%X" % size
        return size.zfill(4)

    #Main workhorse for client, handles all recieved messages
    def readSnac(self,packet):
        self.family = int(packet[0:4], 16)
        self.subtype = int(packet[4:8], 16)                    
        self.flags = packet[8:12]
#        print "Recieved", self.family, self.subtype

        
        if(len(packet) > 12):
            
            self.request = int(packet[12:20], 16)
            data = packet[20:len(packet)]

            if(self.flags[0] == '8'):
                data = data[4:len(data)]
                tlv, data = readtlv(data)
                
            self.data = data

            if(self.family == 1 and self.subtype == 3):
                self.tlvs = self.getSupportedServices()
                vars.services = self.tlvs
            elif(self.family == 1 and self.subtype == 24):
                self.tlvs = self.updateSupportedServices()
                vars.services = self.tlvs
            elif self.family == 1 and self.subtype == 7:
                self.tlvs = self.getRateInfo()
                vars.rates = self.tlvs

                
            elif self.family == 4 and self.subtype == 7:
                self.tlvs = self.getMessage()
            elif self.family == 4 and self.subtype == 1:
                self.tlvs = self.getError()
            elif self.family == 3 and self.subtype == 11:
                
                self.tlvs = self.updateBuddyList(1)
            elif self.family == 3 and self.subtype == 12:
                self.tlvs == self.updateBuddyList(0)
            elif self.family == 19 and self.subtype == 6:
                self.tlvs = self.getBuddyList() 
            elif self.family == 1 and self.subtype == 19:
                #Message of the day, recieved during sign on, but no use
                pass
            elif self.family == 3 and self.subtype == 10:
                pass
#print "rejected buddy"
            elif self.family == 4 and self.subtype == 23:
                pass
#                print "End of Offline Messages"
            elif self.family == 4 and self.subtype == 20:
                self.tlvs = self.getMTN()
            else:
                self.tlvs = self.processSnac()

    #printable version of snac
    def __str__(self):
        return "\nFamily: "+str(self.family)+"\nSubType:"+str(self.subtype)+"\nFlags:"+str(self.flags)+"\nRequest"+str(self.request)+"\nData:"+str(self.data)

    #Packs the snac contents for trasmission
    def pack(self):
        return str(self.family)+str(self.subtype)+str(self.flags)+str(self.request)+str(self.data)        

    #Generic Processing if it is not a specified type type
    def processSnac(self):
        tlvs = []
        data = self.data
        while(len(data) > 0):
            tlv, data = readtlv(data)
            tlvs.append(tlv)
        return tlvs
    

        
    def getMTN(self):
        tlvs = []
        l = int(self.data[20:22], 16)
        sn = unhexlify(self.data[22:22+l*2])
        type = int(self.data[22+l*2:26+l*2], 16)
    
        self.important = [sn, type]
        



    #Reads the rate info
    def getRateInfo(self):
        tlvs = []
        data = self.data
        numRates = int(data[0:4], 16)
        data = data[4:len(data)]

        for i in range(0, numRates):
            r = rate()
            r.readRate(data)
            data = data[70:len(data)]
            tlvs.append(r)
        return tlvs

    #Reads supported services
    def getSupportedServices(self):
        tlvs = []
        data = self.data
        while(len(data)> 0):
            tlvs.append(data[0:4])
            data = data[4:len(data)]
        return tlvs

    #Updates supported services
    def updateSupportedServices(self):
        tlvs =[]
        data = self.data
        while(len(data) > 0):
            tlvs.append([data[0:4], data[4:8]])
            data = data[8:len(data)]
        return tlvs

    #Deals with oncoming/offgoing buddy notificiations
    def updateBuddyList(self, newStatus):
        
        tlvs = []
        data = self.data 

        snLen = int(data[0:2],16)
        sn = unhexlify(data[2:2+snLen*2])
        warning = int(data[2+snLen*2:6+snLen*2],16)
        numtlvs = int(data[6+snLen*2:10+snLen*2],16)
    

        data = data[10+2*snLen:len(data)]
        while len(data) > 0:
            tlv, data = readtlv(data)
            tlvs.append(tlv)


        for ii in tlvs:
            if ii[0] == "0006":
                if ii[2] == "00000001":
                    newStatus = 2
                if ii[2] == "00000000":
                    newStatus = 1
            
            if ii[0] == "001d":
                #Parsing BART data, this part is a bit iffy
                contents = ii[2]
                message = ""
                while len(contents) > 0:
                    t = contents[0:4]
                    n = contents[4:6]
                    l = 2*int(contents[6:8], 16)
                    v = contents[8:8+l]

                    if t == "0002":
                        message = unhexlify(v[4:l-4])
                    contents = contents[8+l:len(contents)]

        if(newStatus == 2):
            pass
            #print "Buddy Away ", sn, message
        if(newStatus == 1):
            pass
            #print "Oncoming Buddy: ", sn, message
        if(newStatus == 0):
#            print "Offgoing Buddy: ", sn
            pass



        for ii in vars.buddyList:
            if ii.name == sn:
                ii.status = newStatus
                ii.message = message
        self.important = [sn, newStatus, message]
        
        
    #Reads buddy list contents
    def getBuddyList(self):

        data =self.data
        vars.listACK = False
        version = data[0:2]
        count = int(data[2:6],16)
        data = data[6:len(data)]
        if vars.wipeList == True:
            vars.buddyList = []
            vars.groupList =[]
            vars.wipeList = False
        

        i = count
        while  len(data) > 8 :
            i = i-1             

            name, groupID, iid, type, data = readSSI(data)

            if type == 0:
                b = buddy(name, groupID, iid)
                vars.buddyList.append(b)
            elif type == 1:
                g = group(name, groupID)
                vars.groupList.append(g)



        lastModified = int(data,16)
        if not lastModified == 0:
            g = group("Offline Buddies", -1)
            vars.groupList.append(g)
            vars.listACK = True
            vars.wipeList = True


    #Deals with most common action, getting a message
    def getMessage(self):
        
        tlvs=[]
        data = self.data
        id = data[0:16]
        channel = data[16:20]
        snLen = data[20:22]
        snLen = int(snLen, 16)

        numtlvs = int(data[26+2*snLen:30+2*snLen], 16)
        data = data[30+2*snLen: len(data)]
        
        off = False
        while len(data) > 0:
           
            tlv, data = readtlv(data)
            tlvs.append(tlv)
            if(tlv[0] == "0002"):
                sn = unhexlify(self.data[22:22+snLen*2])                
                featureLen = int(tlv[2][4:8], 16)
                features = tlv[2][8:8+featureLen*2]
                x = 8+2*featureLen
                blockinfo = tlv[2][x:x+4]
                blockLen = tlv[2][x+4:x+8]
                charSet = tlv[2][x+8:x+12]
                charSubSet = tlv[2][x+12:x+16]
                message = unhexlify(tlv[2][x+16:len(tlv[2])])
                striphtml = re.compile("<.*?>")
                message = striphtml.split(message)
               
                output = ""
                for ii in range(len(message)):
                    output += message[ii]
                self.important = sn + ":" +output
            if tlv[0] == "0006":
                if tlv[1] == "0000":
                    off = True
 
        

                
        if off:
#            print "Offline message from",  sn, ': ', output
            vars.offlineMessages.append([sn,output])
#            print vars.offlineMessages, len(vars.offlineMessages)
            pass
        else:
#            print  sn, ': ', output
            pass

        return tlvs

        

    #Deals with error messages
    def getError(self):
        found = False
        for ii in range(len(vars.msgErrorCodes)):
            if( self.data == vars.msgErrorCodes[ii]):
                #print vars.msgErrors[ii]
                found = True
        if not found:
            #print "Currently unknown error occured"
            pass

    def setICBM(self):
        data = "00000000011b1f4003e703e700000000"
        self.write("4", "2", "01", data)

    #Sends new connections hand shake
    def writeNewConnection(self):
        self.family = ""
        self.subtype =""
        self.flags = ""
        self.request = ""
        self.data = "00000001"

    #Sends authorization cookie
    def writeCookie(self, cookie):
        self.family = ""
        self.subtype =""
        self.flags =""
        self.request =""
        self.data =   "00000001"+tlv("06", cookie)

        
    #Request md5 Key
    def write1706(self, sn):
        self.family = "0017"
        self.subtype = "0006"
        self.flags = "0000"
        self.request = "%X" % vars.seq
        self.request = self.request.zfill(8)
        length = len(sn)
        length = "%X" % length
        sn = hexlify(sn).zfill(2*int(length, 16))
        self.data = "000100" + length.zfill(2) +sn+"004b0000005a0000"
        
    #Send client login request
    def write1702(self, sn, password, key):
        self.family = "0017"
        self.subtype = "0002"
        self.flags = "0000"
        self.request = "%X" %vars.seq
        self.request = self.request.zfill(8)


        #password = md5(password).digest()
        response= md5(key+password+"AOL Instant Messenger (SM)").hexdigest()
        self.data = tlv("1", sn) + tlv("25", unhexlify(response)) + tlv("4c", "")+  tlv("3", "Purple/2.6.2")+tlv("16", 265) + tlv("17", 5) + tlv( "18", 1) + tlv("19", 0) + tlv("1a", 3036) + tlv("14", 1553) + tlv("f", "en") + tlv("e", "us") + tlv("4a", 1)


    #Request server services    
    def write117(self):
        self.family = "0001"
        self.subtype = "0017"
        self.flags="0000"
        self.request = "%X" % vars.seq
        self.request = self.request.zfill(8)
        self.data = ""
        for ii in range(len(vars.capabilities)):
            self.data += vars.capabilities[ii][0].zfill(4) + vars.capabilities[ii][1].zfill(4)

    #Announce Client is ready
    def write102(self):
        self.family = "0001"
        self.subtype = "0002"
        self.flags="0000"
        self.request = "%X" % vars.seq
        self.request = self.request.zfill(8)
        self.data=""

        for ii in range(len(vars.capabilities)):
            
            self.data += vars.capabilities[ii][0].zfill(4) + vars.capabilities[ii][1].zfill(4) + "0110047c"


    def sendMessage(self, rec, message):
        self.family = "0004"
        self.subtype = "0006"
        self.flags="0000"
        self.request = "%X" % vars.seq
        self.request = self.request.zfill(8)
        snLen = "%X" % len(rec)
        snLen = snLen.zfill(2)

        msgLen = "%X" % (len(message)+8)
        msgLen = msgLen.zfill(4)

        cookie = "%X" % vars.msgCookie
        self.data = cookie.zfill(16) + "0001" + snLen + hexlify(rec) + tlv("02", unhexlify("05010004010101020101")+ unhexlify(msgLen) +unhexlify("00000000")+ message + unhexlify("00030000"))

        found, ii = getBuddy(rec)
       
        if found:
            pid = ii.iid 
            id = ii.group
            if ii.status == 0:
                self.data += tlv("6","")
            

    #Request SSI list (I think this isn't used anymore)
    def write1304(self):
        self.write("0013", "0004", "0000", "")

    #Announce SSI Edit (I think this isn't used anymore)
    def startEdit(self):
        self.write("0013", "0011", "0000", "")

    #Annouce SSI Edit Complete (Not used anymore)
    def endEdit(self):
        self.write("0013", "0012", "0000", "")

    #Adds buddy to a list, can specify group or not
    # add buddy group (puts into group "group")
    # add buddy (puts into default group)
    def addBuddy(self, message):
        message = message.split(' ', 1)
        l = len(message[0])
        l = ("%X"%l).zfill(4)
        nm = hexlify(message[0])

        self.write("0013", "0008", "0000", "")
        found = False
        if len(message) >= 2:
            groupName = message[1]
            for ii in vars.groupList:
                if ii.name == groupName:
                    id = ii.id
                    found = True

            if not found:
                #print "Group Name Not Found"
                id = vars.groupList[1].id
        else:
            id = vars.groupList[1].id

        pid = 0
        found = False
        for ii in range(1,200):
            found = False
            for jj in vars.buddyList:
                if ii == jj.iid:
                    found = True
            if not found:
                pid = ii
                b = buddy(message[0], id, pid)
                vars.buddyList.append(b)
                break

        id = ("%X"%id).zfill(4)
        pid = "%X"%pid
        pid = pid.zfill(4)
        self.data = l+nm+id+pid+"00000000"
   
    def sendMTN(self, user, type):
        l = ("%X"%len(user)).zfill(2)
        sn = hexlify(user)
        type = ("%X"%type).zfill(4)
        data = "00000000000000000001"+str(l)+str(sn)+str(type)
        self.write("4", "14", "0", data)


    #Removes a buddy from the list
    def removeBuddy(self,message):
        if(len(message.split(' ')) > 1):
            #print "Bad User Name"
            return False
        l = len(message)
        l = ("%X"%l).zfill(4)
        nm = hexlify(message)
        found = False
        for ii in vars.buddyList:
            if ii.name == message:
                pid = ii.iid
                id = ii.group
                vars.buddyList.remove(ii)
                found = True
        if not found:
            #print "User ", message, " not on buddy list"
            return False
        pid = ("%X"%pid).zfill(4)
        id = ("%X"%id).zfill(4)
        self.write("0013", "000a", "0000", "")
        self.data = l+nm+id+pid+"00000000"
        return True
    
    def setInvisible(self):
        data = tlv("6", unhexlify("00000100"))
        self.write("1", "1E", "0", data)

    #sets user to away status
    def setAway(self):
        data = tlv("6", unhexlify("00000001"))
        self.write("0001", "001E", "0000", data)
        

    #sets users away message
    def setAwayInfo(self, message):
        data =tlv("3",  'text/aolrtf; charset"us-ascii"')+tlv("4", message)
        self.write("0002", "0004", "0000", data)

    #Sets status message (an online away message)
    def setStatus(self, message):

        l = ("%X"%(len(message)+4)).zfill(2)
        l2 = ("%X"%(len(message))).zfill(2)
        message = unhexlify("000204" + l + "00" +  l2) + message + unhexlify("000000090000")
        data = tlv("1d", message)
        self.write("1", "1e", "0", data)

    #Return from away
    def setBack(self):
        data = tlv("6", unhexlify("00000000"))
        self.write("0001", "001E", "0000", data)

    #Generic snac packet
    def write(self, family, subtype, flags, data):
        self.family = family.zfill(4)
        self.subtype = subtype.zfill(4)
        self.flags = flags.zfill(4)
        self.request = "%X" % vars.seq
        self.request = self.request.zfill(8)
        self.data = data

    def getOfflineMessages(self):
        self.write("4", "10", "0", "")

#       self.write("15", "2", "0", "")
 #       self.data = tlv("1", unhexlify("0800000000003c000000"))
    
#Create a type/length/value tuple
def tlv(type, value):
    t_int = int(type, 16)
    words = [8, 9, 12, 13, 22, 23, 24, 25, 26]
    dwords = [70, 64, 20]

    t = type.zfill(4)
    if isinstance(value, int):
        value = "%X" % value
        
        if t_int in words:
            value = value.zfill(4)
        if t_int in dwords:
            value = value.zfill(8)

    
        if(len(value) % 2 == 1):
            value = value.zfill(len(value) + 1)
        l = "%X" % ((len(value))/2)
        l = l.zfill(4)
        return t+l+value

    else:
        #Deals with some types being 4 or 8 bytes (seems weird they'd do this)
        if t_int in words:
            value = value.zfill(4)
        if t_int in dwords:
            value = value.zfill(8)
        l = "%X" %len(value)
        l = l.zfill(4)
        return t+l+hexlify(value)
    
#Parses TLVs
def readtlv(data):


    t = data[0:4]
    l = data[4:8]
    length = int(l, 16)*2
    v = data[8:8+length]

    return [t,l,v], data[8+length:len(data)]


#Reads a buddy list entry
def readSSI(data):

    tlvs = []
    nmLen = int(data[0:4], 16)
    nm = unhexlify(data[4:4+2*nmLen])
    groupID = int(data[4+2*nmLen:8+2*nmLen], 16)
    itemID = int(data[8+2*nmLen:12+2*nmLen], 16)
    flagType = int(data[12+2*nmLen:16+2*nmLen], 16)
    extra = 2*int(data[16+2*nmLen:20+2*nmLen], 16)
    data = data[20+2*nmLen:len(data)]
    tlvdata = data[0:extra]
    while len(tlvdata) > 0:
        tlv, tlvdata = readtlv(tlvdata)
        tlvs.append(tlv)
    data = data[extra:len(data)]
    return nm, groupID, itemID,flagType, data
    
    
